<?php
// @formatter:off
/**
 * @file WCTagsHolder.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-07-18 22:13:05 +0000 (Thu, 18 Jul 2013) $
 *
 * $Id: WCTagsHolder.php 97 2013-07-18 22:13:05Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0/includes/holders/WCTagsHolder.php $
 */
// @formatter:on

require_once "{$wcPaths["includes-directory"]}/representations/WCTag.php";

/**
 * @class WCTagsHolder This class is a holder for representations of table
 * '%tags', in other words, a holder for WCTag objects.
 */
class WCTagsHolder extends WCGHolder {
	/**
	 * @var WCTagsHolder Pointer to the singleton instance.
	 */
	protected static $_Instance;
	/**
	 * @var string Class name of the objects to be held.
	 */
	protected $_PropertiesItemClass = "WCTag";
	/**
	 * @var string This is the prefix of every column on the table
	 * '%tags'.
	 */
	protected $_PropertiesPrefix = "tag_";
	/**
	 * @var string This is the name of the table represented by WCTag
	 * objects. In this case, '%tags'.
	 */
	protected $_PropertiesTable = "tags";
	/**
	 * Class constructor.
	 */
	protected function __construct() {
		//
		// Calling parent method.
		parent::__construct();
	}
	//
	// Public methods.
	/**
	 * This method allows to add a comic to a tag.
	 *
	 * @param WCTag $tag Tag receiving a new member.
	 * @param WCComic $comic Comic to be added.
	 * @return boolean Returns true it was added.
	 */
	public function addTagTo(WCTag &$tag, WCComic &$comic) {
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Checking:
		//	- tag is ok.
		//	- comic is ok.
		//	- tag and comic belong to the same group.
		if($tag->ok() && $comic->ok() && $tag->group == $comic->group) {
			//
			// Adding the comic to the tag.
			$out = $tag->add($comic);
		}
		//
		// Returning addition result.
		return $out;
	}
	/**
	 * This method allows to add all members of a directory to a tag.
	 *
	 * @param WCTag $tag Tag receiving new members.
	 * @param WCDirectory $directory Directory holding the items to be added.
	 * @return boolean Returns true when all members of the directory were
	 * added.
	 */
	public function addTagToDirectory(WCTag &$tag, WCDirectory &$directory) {
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Checking:
		//	- tag is ok.
		//	- directory is ok.
		//	- tag is from the same group than directory.
		if($tag->ok() && $directory->ok() && $tag->group == $directory->group) {
			//
			// Adding the directory to the tag.
			$out = $tag->addDirectory($directory);
		}
		//
		// Returning addition result.
		return $out;
	}
	/**
	 * This method allows to add all members of a tag to another.
	 *
	 * @param WCTag $tag Tag receiving new members.
	 * @param WCTag $tagToAdd Tag holding the items to be added.
	 * @return boolean Returns true when all members were added.
	 */
	public function addTagToTag(WCTag &$tag, WCTag &$tagToAdd) {
		//
		// Setting a default value to be returned.
		$out = false;
		//
		// Checking:
		//	- origin tag is ok.
		//	- destination tag is ok.
		//	- both tags belong to the same group.
		if($tag->ok() && $tagToAdd->ok() && $tag->group == $tagToAdd->group) {
			//
			// Adding the origin tag's elements.
			$out = $tag->addTag($tagToAdd);
		}
		//
		// Returning addition result.
		return $out;
	}
	/**
	 * This method enforces the existence of special tags on every group.
	 */
	public function createSpecialTags() {
		//
		// Loading global pointers.
		global $wcSpecialTags;
		global $wcGroupsHolder;
		//
		// Walking over every group.
		foreach($wcGroupsHolder->groups() as $group) {
			//
			// Walking over every special tag.
			foreach($wcSpecialTags as $name) {
				//
				// Checking if the tag is present.
				if(!$this->findItemByName($name, $group)) {
					//
					// Creating the tag because it's not
					// present.
					$this->create($name, $group);
				}
			}
		}
	}
	/**
	 * This method allows to retrieve tags associated with certain group.
	 *
	 * @param WCGroup $group Group associated with all tags to return.
	 * @param boolean $noSpecials When this parameter is true, special tags
	 * won't be brought into the returning list.
	 * @return WCTag[] List of tags.
	 */
	public function tags(WCGroup &$group, $noSpecials = false) {
		/** @fixme something in here smells wrong. '$this->items()' should bring all tags,
		 * even specials, therefore, '$noSpecials' may be having no effect. */
		//
		// Loading global pointers.
		global $wcSpecialTags;
		//
		// Setting a default value to be returned.
		$out = array();
		//
		// Checking if special tags should be returned.
		if(!$noSpecials) {
			//
			// Searching and adding all special tags.
			foreach($wcSpecialTags as $name) {
				$tag = $this->itemByName($name, $group);
				if($tag) {
					$out[] = $tag;
				}
			}
		}
		//
		// Loading the rest of tags.
		$out = array_merge($out, $this->items($group));
		//
		// Sorting generated list.
		uasort($out, "WCTag::NameSort");
		//
		// Returning a list of tags.
		return $out;
	}
	/**
	 * This method generates a list of tags based on a comic.
	 *
	 * @param WCComic $comic Comic to use as filter.
	 * @return WCTag[] List of tags where the comic is included.
	 */
	public function tagsOf(WCComic &$comic) {
		//
		// Setting a default value to be returned.
		$out = array();
		//
		// Creating a statement for tags of a comic.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  distinct mem_tag\n";
			$query .= "from    {$this->_dbprefix}tag_members\n";
			$query .= "where   mem_group = :group\n";
			$query .= " and    mem_comic = :comic";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		//
		// Building database parameters.
		$params = array(
			":group" => $comic->group,
			":comic" => $comic->id
		);
		//
		// Requesting tag ids.
		if($stmt->execute($params)) {
			//
			// Loading global pointers.
			global $wcGroupsHolder;
			//
			// Creating a shortcut for comic's group.
			$group = $wcGroupsHolder->groupById($comic->group);
			//
			// Walking over every result and creating a list of tags
			// with them.
			foreach($stmt->fetchAll() as $row) {
				$tag = $this->item($row["mem_tag"], $group);
				if($tag) {
					$out[] = $tag;
				}
			}
		}
		//
		// Sorting generated list.
		uasort($out, "WCTag::NameSort");
		//
		// Returning a list of tags.
		return $out;
	}
	/**
	 * This method generates a list of tags based on a directory.
	 *
	 * @param WCDirectory $directory Directory to use as filter.
	 * @return WCTag[] List of tags where the directory's elements are included.
	 */
	public function tagsOfDirectory(WCDirectory &$directory) {
		//
		// Setting a default value to be returned.
		$out = array();
		//
		// Creating a statement for tags of a directory.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  distinct mem_tag\n";
			$query .= "from    {$this->_dbprefix}tag_members\n";
			$query .= "                inner join {$this->_dbprefix}_comics\n";
			$query .= "                        on (cmc_id = mem_comic)\n";
			$query .= "where   mem_group     = :group\n";
			$query .= " and    cmc_directoryid = :directory";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		//
		// Building database parameters.
		$params = array(
			":group" => $directory->group,
			":directory" => $directory->id
		);
		//
		// Requesting tag ids.
		if($stmt->execute($params)) {
			//
			// Loading global pointers.
			global $wcGroupsHolder;
			//
			// Creating a shortcut for directory's group.
			$group = $wcGroupsHolder->groupById($directory->group);
			//
			// Walking over every result and creating a list of tags
			// with them.
			foreach($stmt->fetchAll() as $row) {
				$tag = $this->item($row["mem_tag"], $group);
				if($tag) {
					$out[] = $tag;
				}
			}
		}
		//
		// Sorting generated list.
		uasort($out, "WCTag::NameSort");
		//
		// Returning a list of tags.
		return $out;
	}
	/**
	 * This method generates a list of tags based on a tag.
	 *
	 * @param WCTag $tag Tag to use as filter.
	 * @return WCTag[] List of tags where the tag's elements are included.
	 */
	public function tagsOfTag(WCTag &$tag) {
		//
		// Setting a default value to be returned.
		$out = array();
		//
		// Creating a statement for tags of a tag.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  distinct mem.mem_tag as mem_tag\n";
			$query .= "from    wc_tag_members as mem\n";
			$query .= "                inner join (\n";
			$query .= "                        select mem_comic\n";
			$query .= "                        from    wc_tag_members\n";
			$query .= "                        where   mem_group = :group\n";
			$query .= "                         and mem_tag = :tag) as tmp\n";
			$query .= "                                on (tmp.mem_comic = mem.mem_comic)\n";
			$query .= "where   mem.mem_group =  :group\n";
			$query .= " and    mem.mem_tag   <> :tag";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		//
		// Building database parameters.
		$params = array(
			":group" => $tag->group,
			":tag" => $tag->id
		);
		//
		// Requesting tag ids.
		if($stmt->execute($params)) {
			//
			// Loading global pointers.
			global $wcGroupsHolder;
			//
			// Creating a shortcut for tag's group.
			$group = $wcGroupsHolder->groupById($tag->group);
			//
			// Walking over every result and creating a list of tags
			// with them.
			foreach($stmt->fetchAll() as $row) {
				$tag = $this->item($row["mem_tag"], $group);
				if($tag) {
					$out[] = $tag;
				}
			}
		}
		//
		// Sorting generated list.
		uasort($out, "WCTag::NameSort");
		//
		// Returning a list of tags.
		return $out;
	}
	/**
	 * This method generates a list of tags based on a keyword. In other
	 * words, it looks for tags with a name like '%keyword%'.
	 * Search is a case insensitive on.
	 *
	 * @param string $keyword Word to use as filter.
	 * @param WCGroup $group Where of found tags.
	 * @return WCTag[] List of tags where the tag's elements are included.
	 */
	public function tagsLike($keyword, WCGroup &$group) {
		//
		// Setting a default value to be returned.
		$out = array();
		//
		// Creating a statement for tags with a name like the keywords.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}tags\n";
			$query .= "where   tag_group = :group\n";
			$query .= " and    lower(tag_name) like :filter\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		//
		// Building database parameters.
		$params = array(
			":group" => $group->id,
			":filter" => "%".strtolower($keyword)."%"
		);
		//
		// Requesting tag ids.
		if($stmt->execute($params)) {
			//
			// Walking over every result and creating a list of tags
			// with them.
			foreach($stmt->fetchAll() as $row) {
				$tag = $this->item($row["tag_id"], $group);
				$out[] = $tag;
			}
		}
		//
		// Sorting generated list.
		uasort($out, "WCTag::NameSort");
		//
		// Returning a list of tags.
		return $out;
	}
	//
	// Public class methods.
	/**
	 * This class method allows to get the sole instance of this holder.
	 *
	 * @return WCTagsHolder Returns the singleton instance of this
	 * class.
	 */
	public static function Instance() {
		//
		// Checking if there's no instance yet.
		if(!isset(self::$_Instance)) {
			$c = __CLASS__;
			//
			// Creating the sole instance.
			self::$_Instance = new $c();
		}
		//
		// Returning the class instance.
		return self::$_Instance;
	}
}
?>